#############################################################################
##
## Copyright (C) 2006-2006 Mindcast Productions. All rights reserved.
##
## This file is part of the Universal SQL Navigator - UniSQLNav.
## Find more information in: http://code.google.com/p/unisqlnav/
## Contact at carlosedp@gmail.com
##
## This file may be used under the terms of the GNU General Public
## License version 2.0 as published by the Free Software Foundation
## and appearing in the file LICENSE.GPL included in the packaging of
## this file.  Please review the following information to ensure GNU
## General Public Licensing requirements will be met:
## http://www.gnu.org/licenses/gpl.txt
##
## This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
## WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
##
## $Rev: $
## $Date: $
##
#############################################################################

from PyQt4 import QtCore, QtGui
import xml.etree.ElementTree as ET
import os.path
import re

from ui.internalWindow import internalWindowTemplate
from ui.ui_session import Ui_SessionWindow
from utils.utilities import executable_path, indentElement
from db.backends import DatabaseException, DBQueryError, BACKENDS, BACKEND_FIELDS


class sessionWindow(internalWindowTemplate, Ui_SessionWindow):
    def __init__(self, parent):
        internalWindowTemplate.__init__(self, parent)

        sessionpath = 'data\sessions.xml'
        self.sessionXMLFile = os.path.join( executable_path(), sessionpath )

        self.connect(self.openButton, QtCore.SIGNAL("clicked()"),self.openSession)
        self.connect(self.cancelButton, QtCore.SIGNAL("clicked()"),self.closeWindow)
        self.connect(self.saveSessionButton, QtCore.SIGNAL("clicked()"),self.saveSession)
        self.connect(self.deleteSessionButton, QtCore.SIGNAL("clicked()"),self.deleteSession)
        self.connect(self.loadDatabaseButton, QtCore.SIGNAL("clicked()"),self.loadDB)
        self.connect(self.sessionTable, QtCore.SIGNAL("itemClicked(QTableWidgetItem*)"),self.loadToEdit)
        self.connect(self.sessionTable, QtCore.SIGNAL("itemDoubleClicked(QTableWidgetItem*)"),self.openSession)
        self.connect(self.driverCombo, QtCore.SIGNAL("currentIndexChanged (QString)"),self.driverchanged)
        
        self.populateBackends()
        self.loadSavedSessions()

    def populateBackends(self):
        self.driverCombo.addItem("", QtCore.QVariant(""))
        for backend in BACKENDS:
            self.driverCombo.addItem(backend, QtCore.QVariant(BACKENDS[backend]))
    
    def driverchanged(self, value):
        fields = ['database','host','port','user','password']
        for field in fields:
            if field in BACKEND_FIELDS[str(value)]:
                eval('self.'+field).setEnabled(True)
            else:
                eval('self.'+field).setEnabled(False)
    
    def loadToEdit(self):
        selectedSession = self.sessionTable.item(self.sessionTable.currentRow(),0).text()
        etree = ET.parse(self.sessionXMLFile)
        root = etree.getroot()
        sessions = root.findall('session')
        sess = [sess for sess in sessions if sess.get('name') == selectedSession][0]
        self.sessionName.setText(sess.get('name'))

        self.driverCombo.setCurrentIndex(self.driverCombo.findData(QtCore.QVariant(sess.find('driver').text)))
        
        self.database.setText(sess.find('db').text)
        try:
            self.host.setText(sess.find('host').text)
        except: pass
        try:
            self.port.setText(sess.find('port').text)
        except: pass
        try:
            self.user.setText(sess.find('user').text)
        except: pass
        try:
            self.password.setText(sess.find('password').text.decode('base64_codec'))
        except: pass

        
    def loadSavedSessions(self):
        #TODO - Add error handling if no sessions.xml is found
        etree = ET.parse(self.sessionXMLFile)
        root = etree.getroot()
        sessions = root.findall('session')
        
        # Sets the table
        self.sessionTable.setSortingEnabled(False)
        for row in xrange(self.sessionTable.rowCount()):
            self.sessionTable.removeRow(0)
        self.sessionTable.verticalHeader().hide()
        tableColumns = ["Name", "Driver", "Database", "User"]
        self.sessionTable.setColumnCount(len(tableColumns))
        self.sessionTable.setHorizontalHeaderLabels(tableColumns)
        
        # Loads xml session data
        for session in sessions:
            name = session.get('name')
            fields = ['driver', 'db', 'host', 'port', 'user', 'pass']
            sessiondata = {}
            for field in fields:
                try:
                    sessiondata[field] = session.find(field).text
                except AttributeError:
                    sessiondata[field] = ''
            
            row = self.sessionTable.rowCount()
            self.sessionTable.insertRow(row)
            itemData = [name, sessiondata['driver'],sessiondata['db'],sessiondata['user']]
            for col, data in enumerate(itemData):
                self.sessionTable.setItem(row, col, QtGui.QTableWidgetItem(str(data)))

        self.sessionTable.setSortingEnabled(True)
        self.sessionTable.sortItems(0)
        self.sessionTable.resizeColumnsToContents()
        self.sessionTable.resizeRowsToContents()
            
    def openSession(self):
        try:
            QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
            selectedSession = self.sessionTable.item(self.sessionTable.currentRow(),0).text()
            etree = ET.parse(self.sessionXMLFile)
            root = etree.getroot()
            sessions = root.findall('session')
            sess = [sess for sess in sessions if sess.get('name') == selectedSession][0]
            
            engine = sess.find('driver').text
            backend = __import__('db.%s' % engine, {}, {}, [''])
            
            db = sess.find('db').text
            try:
                host = sess.find('host').text
            except:
                host = ''
            try:
                port = sess.find('port').text
            except:
                port = ''
            try:
                user = sess.find('user').text
            except:
                user = ''
            try:
                pwd = sess.find('password').text.decode('base64_codec')
            except:
                pwd = ''            
           
            session_data = dict((data.tag, data.text) for data in sess.getiterator())
            session_data['name'] = sess.get('name')
            session = backend.Database(db, host,port,user,pwd)
            self.parent.treeAddSession(session, session_data)
            QtGui.QApplication.restoreOverrideCursor()
            self.close()
        except Exception, msg:
            QtGui.QApplication.restoreOverrideCursor()
            QtGui.QMessageBox.critical( self, self.tr('Error'), str(msg),
                                        QtGui.QMessageBox.Ok,QtGui.QMessageBox.NoButton,QtGui.QMessageBox.NoButton)
    
    def closeWindow(self):
        self.close()

    def saveSession(self):
        # Manage duplicate names and edits
        etree = ET.parse(self.sessionXMLFile)
        root = etree.getroot()
        sessions = root.findall('session')

        if str(self.sessionName.text()) in [session.get('name') for session in sessions] \
                or str(self.sessionName.text()) == '' or str(self.database.text()) == '':
            
            QtGui.QMessageBox.warning( self, self.tr('Invalid Name'),"Duplicate or blank session/driver name, choose another one.",
                            QtGui.QMessageBox.Ok,QtGui.QMessageBox.NoButton,QtGui.QMessageBox.NoButton)
            return
        sess = ET.SubElement(root, 'session', name=str(self.sessionName.text()))
        ET.SubElement(sess, 'driver').text = str(self.driverCombo.itemData(self.driverCombo.currentIndex()).toString())
        ET.SubElement(sess, 'db').text = str(self.database.text())
        ET.SubElement(sess, 'host').text = str(self.host.text())
        ET.SubElement(sess, 'port').text = str(self.port.text())
        ET.SubElement(sess, 'user').text = str(self.user.text())
        ET.SubElement(sess, 'password').text = str(self.password.text()).encode('base64_codec')
                
        for element in etree.getiterator():
            indentElement(element)
        
        etree.write(self.sessionXMLFile)
        self.loadSavedSessions()            
        
    def deleteSession(self):
        etree = ET.parse(self.sessionXMLFile)
        root = etree.getroot()
        sessions = root.findall('session')
        selectedSess = self.sessionTable.item(self.sessionTable.currentRow(),0).text()
        sess = [session for session in sessions if session.get('name') == str(selectedSess)][0]
        root.remove(sess)
        etree.write(self.sessionXMLFile)
        self.loadSavedSessions() 

    def loadDB(self):
        if self.driverCombo.itemData(self.driverCombo.currentIndex()).toString() == 'sqlite':
            filePath = QtGui.QFileDialog.getOpenFileName(self, self.tr("Open File"),
                        "", self.tr("SQLite DB files (*.db)"))
            self.database.setText(filePath)
        if self.driverCombo.itemData(self.driverCombo.currentIndex()).toString() == 'oracle9i':
            tnsnames = open(os.path.join(os.getenv('ORACLE_HOME'),r'network\ADMIN\tnsnames.ora'),'r').read()
            from ui.tnsWindow import tnsWindow
            self.parent.openWindow(tnsWindow,self.parseTNS(tnsnames))

    def parseTNS(self, text):
        # Strip comments and blank lines.
        text = re.sub(r'#[^\n]*\n', '\n', text)
        text = re.sub(r'( *\n *)+', '\n', text.strip())
       
        # Split into database entries by balancing the parentheses.
        databases = []
        start = 0
        c = 0
        while c < len(text):
            parens = 0
            c = text.find('(')
            while c < len(text):
                if text[c] == '(':
                    parens += 1
                elif text[c] == ')':
                    parens -= 1
                c += 1
                if parens == 0:
                    break
           
            databases.append(text[start:c].strip())
            text = text[c:]
            c = 0
       
        # Parse each database entry, building a dictionary of dictionaries
        # mapping from database name to database properties.
        database_dicts = {}
        for database in databases:
            # Get the database name and a set of (name, value) pairs.
            name = re.match(r'(\w+)', database).group(1)
            names_and_values = re.findall(r'(?i)([a-z]+)\s*=\s*([a-z0-9-\.]+)', database)
            # Build a dictionary from them, and if it has a HOST, add the IP.
            database_dict = dict(names_and_values)
            #if database_dict.has_key('HOST'):
            #    database_dict['IP'] = socket.gethostbyname(database_dict['HOST'])

            database_dicts[name] = database_dict
        return database_dicts
